package com.txtsqlclient.dataaccess.database;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.txtsqlclient.domain.QueryExecutionException;
import com.txtsqlclient.domain.QueryResult;
import com.txtsqlclient.domain.ReadQueryResult;
import com.txtsqlclient.domain.WriteQueryResult;

/**
 * This class is required to execute various SQL queries using the database
 * connection.
 * 
 * @author spadda
 */
public class QueryExecutionDAO {

	private static final String SELECT = "SELECT";

	private static QueryExecutionDAO instance;

	private static Log LOGGER = LogFactory.getLog(QueryExecutionDAO.class);

	protected QueryExecutionDAO() {
		// empty constructor to enforce access restrictions.
	}

	public static QueryExecutionDAO getInstance() {
		if (instance == null) {
			LOGGER
					.debug("Factory instance does not exist. Creating a new instance.");
			instance = new QueryExecutionDAO();
		}
		return instance;
	}

	/**
	 * This method is required to execute a database query.
	 * 
	 * @param query
	 * @return a list of lists of strings, representing the table of the
	 *         results.
	 */
	public QueryResult execute(final String query, final Connection connection)
			throws QueryExecutionException {
		QueryResult result = null;
		Statement stmt = null;
		boolean isReadQuery = false;
		try {
			stmt = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
					ResultSet.CONCUR_READ_ONLY);
			// Put a special condition for SELECT statements, as some drivers
			// support only executeQuery()
			if (query.toUpperCase().startsWith(SELECT)) {
				result = getReadQueryResult(stmt.executeQuery(query));
				isReadQuery = true;
			} else {
				isReadQuery = stmt.execute(query);
			}
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("Executed " + (isReadQuery ? "READ" : "WRITE")
						+ " query[" + query + "]");
			}
			if (isReadQuery && result == null) {
				result = getReadQueryResult(stmt);
			} else if (!isReadQuery) {
				result = getWriteQueryResult(stmt);
			}
		} catch (SQLException e) {
			throw new QueryExecutionException("Error while executing query:"
					+ query, e);
		}
		LOGGER.trace("Query execution complete");
		return result;
	}

	protected QueryResult getReadQueryResult(final Statement stmt)
			throws QueryExecutionException, SQLException {
		final ResultSet rs = stmt.getResultSet();
		return getReadQueryResult(rs);
	}

	protected QueryResult getReadQueryResult(final ResultSet rs)
			throws QueryExecutionException {
		return new ReadQueryResult(rs);
	}

	protected QueryResult getWriteQueryResult(final Statement stmt)
			throws SQLException {
		final int numOfRowsModified = stmt.getUpdateCount();
		final QueryResult result = new WriteQueryResult(numOfRowsModified);
		return result;
	}

}